(defmacro get-defun (symb)
  `(get ,symb :defun))

(defun meval (expr env)
  (cond ((and (atom expr) (constantp expr))
	 	 expr)

		((symbolp expr)
	 	 (let ((cell (assoc expr env)))
	   	   (if cell
	       	 (cdr cell)
	     	 (error "~s n'est pas un symbole" expr))))

		((and (consp (car expr)) ;si liste
		   	  (eq (caar expr) 'lambda)) ;si 1er elmt == lambda
	 	 (meval-body (cddar expr) ;
		     		 (make-env (cadar expr)
			       			   (meval-args (cdr expr) env)
			       			   env)))

		;(meval-lambda (car expr)
		;     (meval-args (cdr expr) env)
		;         env)

		((not (symbolp (car expr)))
	 	 (error "~s n'est pas un symbole, ou pas une fonction " (car expr)))

		((get-defun (car expr))
	 	 (let ((fun (get-defun (car expr))))
	   	   (meval-body (cddr fun)
		       		   (make-env (cadr fun)
				 				 (meval-args (cdr expr) env)
				 				 nil))))

		;ici on insere les meta definies

		((not (fboundp (car expr)))
	 	 (error "~S est une fonction inconnue." (car expr)))

		;TODO il faut que le nom de la fction soit un symbole
		;donc on le cree
		((eq 'defun (car expr))
	 	 (setf (get-defun (cadr expr))
	       	   `(lambda ,@(cddr expr))))

		((macro-function (car expr))
	 	 (meval (macroexpand-1 expr) env))

		((not (special-form-p (car expr)))
	 	 (apply (symbol-function (car expr))
				(meval-args (cdr expr) env) ))

		((eq 'quote (car expr)) (cadr expr))

		((eq 'if (car expr))
	 	 (if (meval (cadr expr) env)
	       (meval (caddr expr) env)
	   	   (meval (cadddr expr) env)))

		((eq 'let (car expr))
	 	 (meval-let (cadr expr)
		    		(cddr expr)
		    		env))

		((eq 'progn (car expr))
	 	 (progn (cdr expr)))


		(t
	 	  (error "~s NYI" (car expr)))
		;(t (apply (symbol-function (car expr)) (meval-args (cdr expr) env)  ))

		))

(defun meval-lambda (lambd args env)
  (meval-body (cddr lambd)
	      	  (make-env (cadr lambd) 
						args 
						env)))

(defun meval-let (lvar lexpr env)
  (if (atom lvar)
	; on a cree l'environnement, on fait un progn sur les expr
    (meval-body lexpr env)
    (meval-let (cdr lvar) lexpr (acons (caar lvar) (cadar lvar) env))))

;(defun meval-let-expr (lexpr env)
;  (if (atom lexpr)
;	nil
;	(if (atom (cdr
;	(progn 	(print (meval (car lexpr) env))
;			(meval-let-expr (cdr lexpr) env))))

; TROP DUR
;(defun meval-progn (lexpr env)
;  (if (atom lexpr)
;	nil
;	(if (atom (cdr lexpr))



(defun make-env (lsymb lval env)
  (if (atom lsymb)
    (if (atom lval)
	  env
	  (error "make-env : trop d'arguments : ~s" lval))
    (if (atom lval)
	  (error "make-env : pas assez d'arguments pour ~s" lsymb)
      (make-env (cdr lsymb) 
				(cdr lval) 
				(acons (car lsymb) (car lval) env)))))




(defun meval-args (l-expr env)
  (if (atom l-expr)
    nil
    (cons (meval (car l-expr) env) 
	  	  (meval-args (cdr l-expr) env))))

(defun meval-body (l-expr env)
  (if (atom l-expr)
    nil
    (if (atom (cdr l-expr))
	  (meval (car l-expr) env)
      (progn 
		(meval (car l-expr) env)
		(meval-body (cdr l-expr) env)))))

;def fibo(n, f_n_1 = 1, f_n = 0):
;    if (n == 0):  # cas de base
;        return f_n
;    else:         # récurrence
;        return fibo(n - 1, f_n, f_n + f_n_1)

(defun fibo (n f1 f2)
  (if (= n 0)
    f2
    (fibo (- n 1) f2 (+ f1 f2))))

(defun rfibo (n)
  (if (< n 2)
    1
    (+ (rfibo (- n 1)) (rfibo (- n 2)))))
